// 八重洲FT-8x7系列电台CAT数据显示
// By BI3QXJ
#include "ft817.h"
#include <M5StickC.h>
#include <SoftwareSerial.h>

bool debug = true;
bool online = false;
unsigned long offline_time;
uint8_t cycle = 0;
uint8_t screen_num = 0;
bool redraw_flag = false;  // 重绘标记

// 硬件设置部分
const uint8_t rxPin = 36;
const uint8_t txPin = 26;
const uint8_t buttonBPin = 39; // 39为右侧小按键
const uint8_t buttonAPin = 37; // 37为正面大按键
const uint8_t ledPin = 10;
SoftwareSerial ser(rxPin, txPin);

// 全局变量部分
// 上一次设备状态, 被用来判断是否重绘相应部分
uint8_t prev_bar = -1;     // 功能状态栏
uint8_t prev_mode = -1;    // 模式
uint8_t prev_s_val  = -1;  // S表数值
uint8_t prev_p_val = -1;   // 功率数值
uint32_t prev_freq = -1;   // VFO频率, 最低位为10Hz
uint8_t prev_rtx = -1;     // 收发状态
uint8_t prev_hswr = -1;    // 高驻波状态

uint8_t prev_freq_digit[8] = {-1, -1, -1, -1, -1, -1, -1, -1};  // VFO频率各位, 频率数字从低到高

uint8_t next_bar = 0;     // 功能状态栏
uint8_t next_mode = 0;    // 模式
uint8_t next_s_val  = 0;  // S表数值
uint8_t next_p_val = 0;   // 功率数值
uint32_t next_freq = 0;   // VFO频率, 最低位为10Hz
uint8_t next_rtx = 0;     // 收发状态
uint8_t next_hswr = 0;    // 高驻波状态

// 第二屏: 四个仪表
uint8_t bar_width = 10;
uint8_t bar_height = 14;
uint8_t prev_meter_pwr = 0;
uint8_t prev_meter_swr = 0;
uint8_t prev_meter_mod = 0;
uint8_t prev_meter_alc = 0;
uint8_t next_meter_pwr = 0;
uint8_t next_meter_swr = 0;
uint8_t next_meter_mod = 0;
uint8_t next_meter_alc = 0;

// 显示位置配置 {x, y, mask_w, mask_h}

const uint8_t vfo_pos[8][4] = {
    {140, 37, 10, 14}, {128, 37, 10, 14},
    {103, 30, 15, 21}, {85, 30, 15, 21}, {67, 30, 15, 21},
    {42, 30, 15, 21}, {24, 30, 15, 21}, {6, 30, 15, 21}
};
const uint8_t mode_pos[4] = {117, 2, 34, 14};
const uint8_t rtx_pos[4] = {2, 2, 10, 14};
const uint8_t p_val_pos[4] = {86, 2, 22, 14};
const uint8_t s1_val_pos[4] = {20, 2, 22, 14};
const uint8_t s2_val_pos[4] = {44, 2, 34, 14};
const uint8_t bar_pos[4] = {36, 64, 88, 14};

//CAT命令部分
byte cat_cmd[5] = {DUMMY_BYTE, DUMMY_BYTE, DUMMY_BYTE, DUMMY_BYTE, DUMMY_BYTE};
byte cat_ret[5] = {DUMMY_BYTE, DUMMY_BYTE, DUMMY_BYTE, DUMMY_BYTE, DUMMY_BYTE};
uint8_t cat_ret_len = 0;

// ----------------------- 元素绘制部分 -----------------------
void draw_vfo(uint32_t freq) {
  // 绘制频率数字
  // 计算本次频率各位数字
  uint8_t vfo_temp[8];
  for (int n = 0; n < 8; n++){
    vfo_temp[n] = freq % 10;
    freq /= 10;
  }
  
  M5.Lcd.setTextColor(TFT_WHITE);
  M5.Lcd.setTextSize(3);
  prev_freq = freq;
  for (int n = 0; n < 8; n++){
    // 如果该位数字和上一次不同, 则进行重绘
    if (redraw_flag or (vfo_temp[n] != prev_freq_digit[n])) {
      if (n < 2) {
        M5.Lcd.setTextSize(2);
        M5.Lcd.fillRect(vfo_pos[n][0], vfo_pos[n][1], 10, 14, BLACK);
      } 
      else {
        M5.Lcd.setTextSize(3);
        M5.Lcd.fillRect(vfo_pos[n][0], vfo_pos[n][1], 15, 21, BLACK);
      }
      M5.Lcd.setCursor(vfo_pos[n][0], vfo_pos[n][1]);
      M5.Lcd.printf("%d", vfo_temp[n]);
      prev_freq_digit[n] = vfo_temp[n];
    }
  }
}

void draw_mode(uint8_t mode) {
  // 绘制模式标识
  if (redraw_flag or (mode != prev_mode)) {
    prev_mode = mode;
    M5.Lcd.setCursor(mode_pos[0], mode_pos[1]);
    M5.Lcd.setTextSize(2);
    M5.Lcd.setTextColor(ORANGE);
    M5.Lcd.fillRect(mode_pos[0], mode_pos[1], mode_pos[2], mode_pos[3], BLACK);
    switch (mode) {
      case MODE_LSB:  M5.Lcd.println("LSB"); break;
      case MODE_USB:  M5.Lcd.println("USB"); break;
      case MODE_CW:   M5.Lcd.println(" CW"); break;
      case MODE_CWR:  M5.Lcd.println("CWR"); break;
      case MODE_AM:   M5.Lcd.println(" AM"); break;
      case MODE_WFM:  M5.Lcd.println("WFM"); break;
      case MODE_FM:   M5.Lcd.println(" FM"); break;
      case MODE_FMN:  M5.Lcd.println("FMN"); break;
      case MODE_DIG:  M5.Lcd.println("DIG"); break;
      case MODE_PKT:  M5.Lcd.println("PKT"); break;
      case MODE_CW_N: M5.Lcd.println("CWN"); break;
      case MODE_CWR_N: M5.Lcd.println("CWR"); break;
      case MODE_DIG_N: M5.Lcd.println("DIG"); break;
    }
  }
}

void draw_s_meter(uint8_t val) {
  // 输入S表原始数值, 绘制S表: 0-9 S0-S9, 10-15 S9+10 - S9+60
  if (redraw_flag or (val != prev_s_val)) {
    prev_s_val = val;
    M5.Lcd.setTextSize(2);
    // 绘制S表两部分
    M5.Lcd.fillRect(s1_val_pos[0], s1_val_pos[1], s1_val_pos[2], s1_val_pos[3], BLACK);
    M5.Lcd.fillRect(s2_val_pos[0], s2_val_pos[1], s2_val_pos[2], s2_val_pos[3], BLACK);
    M5.Lcd.setCursor(s1_val_pos[0], s1_val_pos[1]);
    if (val < 10) {
      M5.Lcd.setTextColor(WHITE);
      M5.Lcd.printf("S%d", val);
    } else {
      //S9以上:S9字样为红色
      M5.Lcd.setTextColor(RED);
      M5.Lcd.printf("S9");
      M5.Lcd.setCursor(s2_val_pos[0], s2_val_pos[1]);
      M5.Lcd.setTextColor(WHITE);
      M5.Lcd.printf("\+%d", (val - 9) * 10);
    }
  }
}

void draw_p_meter(uint8_t val) {
  // 打印功率标识
  if (redraw_flag or (val != prev_p_val)) {
    prev_p_val = val;
    M5.Lcd.fillRect(p_val_pos[0], p_val_pos[1], p_val_pos[2], p_val_pos[3], BLACK);
    M5.Lcd.setTextSize(2);
    M5.Lcd.setTextColor(TFT_WHITE);
    switch (val) {
      case 0: M5.Lcd.drawString("H", p_val_pos[0], p_val_pos[1]);break;
      case 1: M5.Lcd.drawString("L3", p_val_pos[0], p_val_pos[1]);break;
      case 2: M5.Lcd.drawString("L2", p_val_pos[0], p_val_pos[1]);break;
      case 3: M5.Lcd.drawString("L1", p_val_pos[0], p_val_pos[1]);break;
    }
  }
}

void draw_status_bar(uint8_t bar_index) {
  // 绘制功能指示栏
  if (redraw_flag or (bar_index != prev_bar)) {
    prev_bar = bar_index;
    M5.Lcd.fillRect(bar_pos[0], bar_pos[1], bar_pos[2], bar_pos[3], BLACK);
    M5.Lcd.drawXBitmap(bar_pos[0], bar_pos[1], status_bar[bar_index], barWidth, barHeight, TFT_WHITE);
  }
}

void draw_rtx(uint8_t rtx) {
  // 绘制RX/TX状态0: RX, 1:TX
  if (redraw_flag or (rtx != prev_rtx)) {
    prev_rtx = rtx;
    M5.Lcd.setTextSize(2);
    M5.Lcd.fillRect(rtx_pos[0], rtx_pos[1], rtx_pos[2], rtx_pos[3], BLACK);
    if (rtx == 1) {
      // digitalWrite(ledPin, HIGH);
      M5.Lcd.setTextColor(GREEN);
      M5.Lcd.drawString("R", rtx_pos[0], rtx_pos[1]);
    } else {
      // digitalWrite(ledPin, LOW);
      M5.Lcd.setTextColor(RED);    
      M5.Lcd.drawString("T", rtx_pos[0], rtx_pos[1]);
    }
  }
}

void draw_bg1(void) {
  // 绘制背景图
  M5.Lcd.setTextSize(2);
  M5.Lcd.drawString(".", 56, 37);
  M5.Lcd.drawString(".", 117, 37);
}

// ----------------------- CAT读取部分 -----------------------
// 公共函数部分, 命令为串行执行, 首先将命令放入cat_cmd, 然后调用cmd_rw/cmd_w命令,
// 返回结果在cat_ret中, 返回数据长度在cat_ret_len
void cmd_w(byte byte0, byte byte1, byte byte2, byte byte3, byte byte4) {
  // 执行"写入"命令
  if (byte4 != 0xbc and byte4 != 0xbe) {
    ser.flush();
    ser.write(byte(byte0));
    ser.write(byte(byte1));
    ser.write(byte(byte2));
    ser.write(byte(byte3));
    ser.write(byte(byte4));
  } else {
    // BC或BE命令不被执行
    Serial.print("Dangerous Command.");
  }
}

void cmd_rw(byte byte0, byte byte1, byte byte2, byte byte3, byte byte4, uint8_t read_len = 5) {
  // 执行"写入+读取"命令
  cmd_w(byte0, byte1, byte2, byte3, byte4);
  delay(10);
  if (ser.available() == 0) {
    Serial.println("Read nothing.\n");
    online = false;
  } else {
    ser.readBytes(cat_ret, read_len);
    // 结果读取到ret中
    // memcpy(cat_ret, ret, sizeof(byte) * 5);
    online = true;
  }
}

void eeprom_read(byte msb, byte lsb) {
  // 读取指定地址的eeprom内容
  cmd_rw(msb, lsb, byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(0xBB), 2);
}

void read_freqmode(void) {
  cmd_rw(byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(FREQMODE_READ));
  // 处理cat_ret中的数据
  unsigned long result(0);
  for (int i = 0; i < 4; i++) {
    result = result * 100 + (cat_ret[i] >> 4) * 10 + (cat_ret[i] & 15);
  }
  next_freq = result;
  next_mode = cat_ret[4];
}

void read_s_val(void) {
  // 读取接收状态S表
  cmd_rw(byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(RXSTATUS_READ));
  next_s_val = cat_ret[0] & 0x0F;
  if (next_rtx == 0) {next_s_val = 0;}
}

void read_p_val(void) {
  // 读取发射功率参数, 0079位置0,1位
  eeprom_read(byte(0x00), byte(0x79));
  next_p_val = cat_ret[0] & 0x03;
}

void read_bar_num(void) {
  // 读取F功能菜单标识, 0076位置0-3位, 只有按下F键选中后才会生效
  eeprom_read(byte(0x00), byte(0x76));
  next_bar = cat_ret[0] & 0x0F;
  if (next_bar > 11) {next_bar = prev_bar;}
}

void read_rtx(void) {
  // 读取PTT状态, 读取TX_STATUS_READ命令返回最高位
  cmd_rw(byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(TXSTATUS_READ));
  next_rtx = ((cat_ret[0] & 0x80) >> 7);
}

void read_meter(void) {
  // 读取PWR/VSWR/MOD/ALC表
  //发射状态返回2个字节, 接收状态返回1个字节
  cmd_rw(byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(DUMMY_BYTE), byte(TXMETER_READ), 2);
  next_meter_pwr = ((cat_ret[0] & 0xF0) >> 4);
  next_meter_alc = cat_ret[0] & 0x0F;
  next_meter_swr = ((cat_ret[1] & 0xF0) >> 4);
  next_meter_mod = cat_ret[1] & 0x0F;

  next_meter_pwr = min(next_meter_pwr, 8);
  next_meter_alc = min(next_meter_alc, 8);
  next_meter_swr = min(next_meter_swr, 8);
  next_meter_mod = min(next_meter_mod, 8);

  if (next_meter_swr >= 6) {digitalWrite(ledPin, LOW);} // 高驻波情况点亮LED
}

void fake_data() {
  // 生成测试数据
  next_freq++;
  next_bar++;
  next_mode++;
  next_s_val++;
  next_p_val++;

  next_rtx = 1 - next_rtx;
  if (next_bar > 11) {next_bar = 0;}
  if (next_mode > 11) {next_mode = 0;}
  if (next_s_val > 15) {next_s_val = 0;}
  if (next_p_val > 3) {next_p_val = 0;}
}

void draw_meter_bar(uint8_t row, uint8_t meter_val, uint16_t color) {
  // 使用指定颜色, 在指定行绘制特定数量的格子, 最大8格
  M5.Lcd.fillRect(40, (4 + 20 * row), 120, 14, TFT_BLUE);
  for (int i = 0; i < meter_val; i++) {
    M5.Lcd.fillRect((40 + i * 13), (4 + 20 * row), 11, 14, color);
  }
}

void draw_meter(uint8_t pwr_val, uint8_t swr_val, uint8_t mod_val, uint8_t alc_val) {
  // 根据仪表读数, 绘制仪表
  if (redraw_flag or (pwr_val != prev_meter_pwr)) {
    draw_meter_bar(0, pwr_val, TFT_WHITE);
    prev_meter_pwr = pwr_val;
  }
  if (redraw_flag or (swr_val != prev_meter_swr)) {
    draw_meter_bar(1, swr_val, TFT_WHITE);
    prev_meter_swr = swr_val;
  }
  if (redraw_flag or (mod_val != prev_meter_mod)) {
    draw_meter_bar(2, mod_val, TFT_WHITE);
    prev_meter_mod = mod_val;
  }
  if (redraw_flag or (alc_val != prev_meter_alc)) {
    draw_meter_bar(3, alc_val, TFT_WHITE);
    prev_meter_alc = alc_val;
  }
}
void simple_analyzer() {
  uint8_t freq_count = 200;
  uint32_t cur_freq = 0;
  uint32_t start_freq = next_freq - (freq_count / 2 * 100);   // 测量间隔为500hz
  uint8_t v[8] = {0,0,0,0,0,0,0,0};

  for(int i = 0; i <= freq_count; i++) {
    // 将频率转换为四HEX字节
    cur_freq = start_freq + 100 * i;
    
    for (int n = 0; n < 8; n++){
      v[n] = cur_freq % 10;
      cur_freq /= 10;
    }
    cmd_w(byte(v[7] * 16 + v[6]), byte(v[5] * 16 + v[4]), byte(v[3] * 16 + v[2]), byte(v[1] * 16 + v[0]), byte(0x01));
    delay(100);
  }
  Serial.println("FFFF");
}

void draw_screen(uint8_t screen_idx) {
  // 根据当前的屏幕编号, 绘制相应屏幕
  // 0: 默认主屏, 1: 仪表, 2: 开关
  switch (screen_idx) {
    case 0:
//      fake_data();    // 模拟数据
      // 获取当前设备状态, 并放入next_变量
      read_freqmode();
      draw_vfo(next_freq);          // 绘制频率
      if (redraw_flag or (cycle % 5 == 2)) {
        read_p_val();
        read_s_val();
        draw_s_meter(next_s_val);   // 绘制S表
        draw_p_meter(next_p_val);   // 绘制功率标识
      }
      if (redraw_flag or (cycle % 5 == 3)) {
        read_rtx();
        draw_rtx(next_rtx);           // 绘制TRX标识
        draw_mode(next_mode);         // 绘制模式
      }
      if (redraw_flag or (cycle % 5 == 4)) {
        read_bar_num();
        draw_status_bar(next_bar);    // 绘制状态条
      }
      break;
    case 1:
      // 读取仪表并绘制
      read_rtx();
      if (next_rtx == 1) {
        draw_meter(0, 0, 0, 0);
        digitalWrite(ledPin, HIGH);
      } else {
        read_meter();
        draw_meter(next_meter_pwr, next_meter_swr, next_meter_mod, next_meter_alc);
      }
      break;
    case 2:
      if (redraw_flag) {
        M5.Lcd.setTextSize(2);
        M5.Lcd.setTextColor(TFT_WHITE);
        M5.Lcd.drawString("SCREEN 3", 30, 30);
      }
      break;
  }
  redraw_flag = false;
}

void draw_background(uint8_t screen_idx) {
  // 根据当前的屏幕编号, 绘制屏幕背景图
  switch (screen_idx) {
    case 0: // 主屏
      M5.Lcd.fillScreen(TFT_BLACK);
      M5.Lcd.setTextSize(2);
      M5.Lcd.drawString(".", 56, 37);
      M5.Lcd.drawString(".", 117, 37);
      break;
    case 1: // 仪表
      M5.Lcd.fillScreen(TFT_BLUE);
      M5.Lcd.setTextColor(TFT_WHITE);
      M5.Lcd.setTextSize(2);
      M5.Lcd.drawString("PWR", 2, 4);
      M5.Lcd.drawString("SWR", 2, 24);
      M5.Lcd.drawString("MOD", 2, 44);
      M5.Lcd.drawString("ALC", 2, 64);
      break;
    case 2: // 开关
      M5.Lcd.fillScreen(TFT_RED);
      break;
  }
}

void switch_screen() {
  // 循环更改屏幕编号, 目前一共3屏
  // 发生切换后绘制背景, 并设置重绘标志
  screen_num ++;
  if (screen_num > 1) {
    screen_num = 0;
  }
  draw_background(screen_num);
  redraw_flag = true;
}

// ----------------------- 主函数 -----------------------
void setup() {
  // 初始化显示
  M5.begin();                   // 初始化设备
  M5.Axp.ScreenBreath(8);       // 设置屏幕亮度
  M5.Lcd.setRotation(3);        // 设置屏幕方向
  M5.Lcd.fillScreen(TFT_BLACK); // 填充黑背景

  pinMode(ledPin, OUTPUT);      // 设置LED引脚
  pinMode(buttonAPin, INPUT);   // 设置大按键引脚
  pinMode(buttonBPin, INPUT);   // 设置侧按键引脚
  digitalWrite(ledPin, HIGH);   // 关闭LED灯

  // 初始化调试端口(USB连接电脑)
  // 初始化软串口(GPIO连设备)
  Serial.begin(115200);
  ser.begin(38400, SWSERIAL_8N1);
  ser.setTimeout(10);
  // 按照38400波特率计算, 每byte传输耗时约0.25ms, 每次CAT指令交互耗时1.3ms+5ms(等待写)
  // 每秒可执行命令交互约100~150次

  draw_background(screen_num);
}

void loop() {
  // 循环函数:  检查按键状态, 绘制相应屏幕内容
  cycle++;
  if (cycle > 30) {cycle = 0;}

  M5.update();
  if (M5.BtnA.wasPressed()) {
    switch_screen();  
  }

  draw_screen(screen_num);
  delay(60);
}
